<script setup lang="ts">
  import { computed, ref } from 'vue'
  import { useStockStore } from '@/stores/stockStore.ts'
  import { useStrategyStore } from '@/stores/strategyStore.ts'

  const stockStore = useStockStore()
  const strategyStore = useStrategyStore()

  // 说明
  const informationDialogStatus = ref(false)
  const onInformationChipClick = () => {
    informationDialogStatus.value = true
  }

  // 判断是否显示chart
  const is_showChart = computed(() => {
    const upper = stockStore.stockIntradayMovement.UpperBound ?? {}
    const lower = stockStore.stockIntradayMovement.LowerBound ?? {}

    const isUpperEmpty = Object.keys(upper).length === 0
    const isLowerEmpty = Object.keys(lower).length === 0
    return !(isUpperEmpty && isLowerEmpty)
  })

  const optionChart = computed(() => {
    const getSignalLabel = (signal: number): string => {
      if (signal === 1) return '买入'
      if (signal === -1) return '卖出'
      return '中性'
    }

    const upper = stockStore.stockIntradayMovement.UpperBound ?? {}
    const lower = stockStore.stockIntradayMovement.LowerBound ?? {}

    const times = Object.keys(upper)
    const upperData = times.map(t => upper[t])
    const lowerData = times.map(t => lower[t])
    const areaData = times.map(t => upper[t] - lower[t])

    const newsData = strategyStore.analyzeIntradayMovementList.map(item => ({
      value: [item.time, item.news], // ✅ 不要 formatTime
      signal: item.signal,
      itemStyle: {
        color:
          item.signal === 1
            ? '#FF0000'
            : (item.signal === -1
              ? '#00FF00'
              : '#AAAAAA'),
      },
    }))

    const vmapData = strategyStore.analyzeIntradayMovementList.map(item => ({
      value: [item.time, item.vmap],
      itemStyle: {
        color: '#FFA500', // 橙色，VAMP 专用颜色
      },
    }))

    const allYValues: number[] = []

    for (const t of times) {
      const up = upper[t]
      const low = lower[t]
      allYValues.push(up)
      allYValues.push(low)
    }

    // 追加 newsData 的 y 值
    for (const nd of newsData) {
      const val = nd.value[1]
      if (!Number.isNaN(val)) {
        allYValues.push(val)
      }
    }
    const minY = Math.min(...allYValues)
    const maxY = Math.max(...allYValues)
    const yPadding = (maxY - minY) * 0.1 || 0.5// 至少留 0.5 空间

    return {
      xAxis: {
        type: 'category',
        data: times,
        name: '时间',
        axisLabel: {
          fontSize: 14,
          color: '#FEFEFE',
          rotate: 45,
        },
        nameTextStyle: {
          fontSize: 14,
          color: '#FEFEFE',
          fontWeight: 'bold',
        },
      },
      yAxis: {
        type: 'value',
        name: '值',
        min: Math.round((minY - yPadding) * 100) / 100,
        max: Math.round((maxY + yPadding) * 100) / 100,
        axisLabel: {
          fontSize: 14,
          color: '#FEFEFE',
        },
        nameTextStyle: {
          fontSize: 14,
          color: '#FEFEFE',
          fontWeight: 'bold',
        },
        splitLine: {
          lineStyle: {
            type: 'dashed',
            color: '#ddd',
          },
        },
      },
      tooltip: {
        trigger: 'axis',
        formatter: (params: any) => {
          const time = params[0].axisValue
          const upperVal = upper[time]
          const lowerVal = lower[time]

          let res = `${time}<br/>
        <span style="color:#f56c6c;">●</span> 上边界: ${upperVal.toFixed(4)}<br/>
        <span style="color:#67c23a;">●</span> 下边界: ${lowerVal.toFixed(4)}<br/>`
          const vmapParam = params.find((p: any) => p.seriesName === 'VAMP')
          if (vmapParam) {
            res += `<span style="color:${vmapParam.color};">●</span> VAMP: ${vmapParam.data.value[1].toFixed(2)}<br/>`
          }
          const newsParam = params.find((p: any) => p.seriesName === '实时价格')
          if (newsParam) {
            res += `<span style="color:${newsParam.color};">●</span> 实时价格: ${newsParam.data.value[1].toFixed(2)} (${getSignalLabel(newsParam.data.signal)})<br/>`
          }

          return res
        },
      },
      legend: {
        data: ['平衡区域', '上边界', '下边界', 'VAMP', '实时价格'],
        textStyle: {
          fontSize: 14,
          color: '#FEFEFE',
        },
        top: '5%',
      },
      series: [
        {
          name: '下边界（基准）',
          type: 'line',
          data: lowerData,
          stack: 'bound',
          legendHoverLink: false,
          lineStyle: { width: 0 },
          showSymbol: false,
          areaStyle: { opacity: 0 },
          color: '#67c23a', // 固定颜色
        },
        {
          name: '平衡区域',
          type: 'line',
          data: areaData,
          stack: 'bound',
          lineStyle: { width: 0 },
          showSymbol: false,
          areaStyle: {
            opacity: 0.3,
            color: '#42A5F5',
          },
          color: '#42A5F5',
        },
        {
          name: '上边界',
          type: 'line',
          data: upperData,
          lineStyle: { width: 2, color: '#f56c6c' },
          symbol: 'circle',
          color: '#f56c6c',
        },
        {
          name: '下边界',
          type: 'line',
          data: lowerData,
          lineStyle: { width: 2, color: '#67c23a' },
          symbol: 'circle',
          z: 10,
          color: '#67c23a',
        },
        {
          name: 'VAMP',
          type: 'line',
          data: vmapData,
          lineStyle: { width: 2, type: 'dashed', color: '#FFA500' },
          symbol: 'circle',
          color: '#FFA500',
          z: 12,
          symbolSize: 6,
          showSymbol: true,
        },
        {
          name: '实时价格',
          type: 'line',
          data: newsData,
          lineStyle: { width: 2, color: '#FFD700' },
          symbol: 'circle',
          z: 15,
          color: '#FFD700',
          symbolSize: 10,
          showSymbol: true,
        },
      ],
      grid: {
        top: '20%',
        left: '10%',
        right: '10%',
        bottom: '15%',
      },
    }
  })
  const selectCode = ref('')
  const selectWindow = ref('')
  const selectPeriod = ref('')
  const snackbarMes = ref('')
  const snackbarStatus = ref(false)
  const onHandleGetIntradayMovementBtnClick = async () => {
    strategyStore.analyzeIntradayMovementList = []
    snackbarMes.value = await stockStore.getStockIntradayMovement(selectCode.value, selectWindow.value, selectPeriod.value)
    snackbarStatus.value = true
  }
  const onHandleAnalyzeIntradayMovementBtnClick = () => {
    strategyStore.analyzeIntradayMovementStrategy(selectCode.value, selectWindow.value, selectPeriod.value)
  }

</script>

<template>
  <v-container>
    <v-row>
      <v-card
        class="w-100 ma-4"
        hover
        variant="outlined"
      >
        <template #subtitle>
          <v-container>
            <v-row>
              <text>日内动量监测分析</text>
              <v-spacer />
              <v-chip
                prepend-icon="mdi-information"
                @click="onInformationChipClick"
              >
                说明
              </v-chip>
              <v-snackbar
                v-model="snackbarStatus"
              >
                {{ snackbarMes }}
                <template #actions>
                  <v-btn
                    color="pink"
                    variant="text"
                    @click="snackbarStatus = false"
                  >
                    Close
                  </v-btn>
                </template>
              </v-snackbar>
              <v-dialog
                v-model="informationDialogStatus"
                transition="dialog-bottom-transition"
                width="auto"
              >

                <v-card
                  prepend-icon="mdi-information"
                  title="日内动量监测分析策略: "
                >
                  <template #text>
                    <p>&nbsp;&nbsp;&nbsp;&nbsp;此策略在获取动量数据的基础上,加入了对当天的日内动量数据进行监测的功能, 可以根据日内动量的实时数据来<strong>判断买入点和卖出点</strong>。
                      在卖出点的处理上, 基于实盘与收益率等多个参数的考量, 会基于上边界和<strong>VMAP</strong>参数来作为判断的关键, 详细内容见下面的VMAP参数说明和卖出点的介绍。</p>
                    <br>
                    <h2>VMAP参数说明: </h2>
                    <p>&nbsp;&nbsp;&nbsp;&nbsp;交易量加权平均价格(VWAP)来源于<em>Carlo Zarattini1, Andrew Aziz2,3, Andrea Barbon的Volume Weighted Average Price (VWAP)
                      The Holy Grail for Day Trading Systems论文</em>。</p>
                    <p>&nbsp;&nbsp;&nbsp;&nbsp;VWAP是一种金融指标, 用于确定某一证券(如股票)在特定时期(通常为日内)的平均价格，该指标同时考虑了这段时间内的<strong>价格变化和交易量</strong>。这个指标通过整合交易量数据来强调交易活跃度较高的时段，从而全面反映市场价格走势。与不考虑交易量变化的简单平均价格计算相比，VWAP提供了更精确的证券平均价格衡量标准。</p>
                    <p>&nbsp;&nbsp;&nbsp;&nbsp;其计算公式如下图: </p>
                    <p><img alt="" src="https://zhixuntrader-pic.oss-cn-shenzhen.aliyuncs.com/result%20%281%29.jpeg"></p>
                    <p>&nbsp;&nbsp;&nbsp;&nbsp;公式中用一天每个时间段的累计价格变化(由最高价, 最低价和最新价组成), 累计交易量作为元素进行计算, 将对应时刻的VMAP值与实时价格相比较, 如果当前市场价格高于VMAP, 这表示市场价格趋势是看涨的;
                      反之, 如果价格低于VMAP值, 说明市场价格趋势是看跌的。</p>
                    <br>
                    <h2>参数说明: </h2>
                    <ul>
                      <li>
                        <strong>股票代码(code): 必填</strong> 填入需要获取日内动量数据的股票代码 如: <em>000421</em>
                      </li>
                      <li>
                        <strong>时间窗口(window): 选填 默认为14天</strong> 填入需要参考的日期时间窗口 如: <em>14</em>
                      </li>
                      <li>
                        <strong>周期(period): 选填 默认为15分钟 可选:<em>15 30</em></strong> 填入当日历史价格记录的时间间隔 如: <em>15</em>
                      </li>
                    </ul>
                    <br>
                    <h2>操作说明: </h2>
                    <ol>
                      <li>
                        按参数说明填入对应的参数
                      </li>
                      <li>
                        点击<strong>获取动量数据</strong>的按钮
                      </li>
                      <li>
                        在下方即可看到对应的图像分析结果
                        <p>
                          <img alt="" src="https://zhixuntrader-pic.oss-cn-shenzhen.aliyuncs.com/Snipaste_2025-07-13_15-49-03.png" width="800">
                        </p>
                      </li>
                      <li>
                        在获取了动量数据后,点击<strong>日内动量监测按钮</strong>,即可每隔<strong>周期(period) 如<em>15分钟</em></strong>监测一次, 在此时间节点的数据也会一起计算出来, 后面的时间则会进行实时监测。
                        <p>
                          <img alt="" src="https://zhixuntrader-pic.oss-cn-shenzhen.aliyuncs.com/Snipaste_2025-07-15_20-34-33.png" width="800">
                        </p>
                      </li>
                    </ol>
                    <br>
                    <h2>卖出点说明: </h2>
                    <br>
                    <p><img alt="" src="https://zhixuntrader-pic.oss-cn-shenzhen.aliyuncs.com/Snipaste_2025-07-13_15-30-10.png"></p>
                    <br>
                    <p>&nbsp;&nbsp;&nbsp;&nbsp;如上图所示, 如果按照原始的下边界突破判断, 由于时间间隔的过滤, 会导致在极端情况下, 市场价格的突然下降造成极大的损失风险。
                      为了降低这个风险又尽可能的保证收益率, 在该策略中把卖出点的判断改为由上边界突破到平衡区或者突破VMAP的下面, 两者综合起来考量, 如下图所示。</p>
                    <br>
                    <p><img alt="" src="https://zhixuntrader-pic.oss-cn-shenzhen.aliyuncs.com/Snipaste_2025-07-13_15-39-59.png"></p>
                    <br>
                    <p>&nbsp;&nbsp;&nbsp;&nbsp;由上图可以看到, 加入了VMAP之后, 会提高在价格下降到下边界之前截止的<strong>概率</strong>。</p>
                    <br>
                    <h2>注意说明: </h2>
                    <ol>
                      <li>需要先获取动量数据, 再进行监测。</li>
                      <li>为避免监测时数据的丢失, 需要保持这个页面, 如果需要进行其他的功能, 可以另外开一个网页或者浏览器界面。</li>
                      <li>周期与时间窗口的设置都会造成买卖信号的改变, 不同股票需要根据具体情况做适量的改变。</li>
                      <li><strong>监测中产生的买卖信号仅供参考, 反映的是可能是最为保守的情况, 不能作为实盘买卖的标准。</strong></li>
                      <li><strong>该策略对于突发市场行情的适应性极差, 可能会出现错过短暂的上涨而买入最高点的情况, 尤其需要注意。</strong></li>
                    </ol>
                    <br>
                    <h2><strong>按<em>ESC</em>退出此说明</strong></h2>
                  </template>
                </v-card>
              </v-dialog>
            </v-row>
          </v-container>
        </template>
        <v-form>
          <v-container>
            <v-row>
              <v-col>
                <v-text-field v-model="selectCode" label="股票代码" required variant="outlined" />
              </v-col>
              <v-col>
                <v-text-field v-model="selectWindow" label="时间窗口" required variant="outlined" />
              </v-col>
              <v-col>
                <v-text-field v-model="selectPeriod" label="周期" required variant="outlined" />
              </v-col>
            </v-row>
            <v-row>
              <v-col class="d-flex justify-end align-center" cols="12">
                <v-btn class="mr-4" prepend-icon="mdi-magnify" variant="outlined" @click="onHandleGetIntradayMovementBtnClick">获得动量数据</v-btn>
                <v-btn class="mr-2" prepend-icon="mdi-magnify" variant="outlined" @click="onHandleAnalyzeIntradayMovementBtnClick">日内动量监测</v-btn>
              </v-col>
            </v-row>
            <v-row v-if="is_showChart">
              <v-divider class="mt-4 mb-4" thickness="2" />
              <e-charts class="w-100" :option="optionChart" style="height: 500px;" />
            </v-row>
          </v-container>
        </v-form>
      </v-card>
    </v-row>
  </v-container>
</template>

<style scoped lang="scss">

</style>
